﻿using System.Data;
using System.Reflection;
using System.Text.Json.Serialization;
using System.Xml;
using System.Xml.Linq;

namespace LightCAD.Core
{

    public class LcadObject
    {
        [JsonInclude]
        public int Id { get; internal set; } = -1;

        protected LcadObject()
        {
        }

        public void Initilize(LcDocument document)
        {
             this.Document = document;
            if(this.Document!= null)
            {
                Id = document.IdCreator.GetObjectId();
            }
        }

        public PropertyGroupCollection ExtPropGroups { get; set; }

        [JsonIgnore]
        public LcDocument Document { get; internal set; }

        [JsonIgnore]
        public LcadObject Parent { get; internal set; }
        [JsonIgnore]
        public string ObjectName
        {
            get { return this.GetType().FullName; }
        }
        protected virtual void OnPropertyChange(string propertyName,PropertyGroup extPropGroup,Property extProp)
        {
        }
        /// <summary>
        /// 用反射方式设置属性
        /// </summary>
        /// <param name="name"></param>
        /// <param name="value"></param>
        /// <param name="fireChangedEvent"></param>
        public void SetProperty(string name, object value,bool fireChangedEvent=true)
        {
            var objType = this.GetType();
            if (!fireChangedEvent)
            {
                var fld = objType.GetField(name);
                if(fld!=null)
                    fld.SetValue(this, value);
                else
                {
                    var prop = objType.GetProperty(name);
                    prop.SetValue(this, value);
                }
                OnPropertyChange(name,null,null);
            }
            else
            {
                Object oldValue=null;
                PropertyInfo prop=null;
                var fld = objType.GetField(name);
                if (fld != null)
                    oldValue=fld.GetValue(this);
                else
                {
                    prop = objType.GetProperty(name);
                    if(prop!=null)
                        oldValue=prop.GetValue(this);
                }
                if (oldValue == value) return;

                PropertyChangedBefore(name, oldValue, value); 
                
                if (fld != null)
                    fld.SetValue(this, value);
                else if(prop!=null)
                {
                    prop.SetValue(this, value);
                }
                else
                {
                    throw new ArgumentException($"Property {name} is not exist.");
                }
                PropertyChangedAfter(name, oldValue, value);
                OnPropertyChange(name,null,null);
            }
        }
        /// <summary>
        /// 用反射方式获取属性值
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public object GetProperty(string name)
        {
            var objType = this.GetType();
            var fld = objType.GetField(name);
            if (fld != null)
                return fld.GetValue(this);
            else
            {
                var prop = objType.GetProperty(name);
                if (prop != null)
                    return prop.GetValue(this);
            }
            return null;
        }

        [JsonIgnore]
        public ObjectPropertiesDefinition PropsDef { get; set; }

        private void InitExtPropDefs()
        {
            var objName = this.ObjectName;
            var doc = this.Document;
            doc.ObjectPropertiesMap.TryGetValue(objName, out ObjectPropertiesDefinition propsDef);
            this.PropsDef = propsDef;
        }
        public PropertyGroup GetExtPropGroup(string groupName)
        {
            if (PropsDef == null)
            {
                InitExtPropDefs();
            }
            if (this.ExtPropGroups == null)
            {
                this.ExtPropGroups = new PropertyGroupCollection();
            }
            PropertyGroup propGrp;
            if (!this.ExtPropGroups.TryGetValue(groupName, out propGrp))
            {
                propGrp = new PropertyGroup
                {
                    Definition = PropsDef.GroupDefs[groupName],
                    Properties = new PropertyCollection()
                };
                this.ExtPropGroups.Add(propGrp);
            }
            return propGrp;
        }

        public Property GetExtPrperty(string name)
        {
            var propGroup = this.GetExtPropGroup(string.Empty);
            return propGroup.GetProperty(name);
        }
        public Property GetExtPrperty(string groupName, string name)
        {
            var propGroup = this.GetExtPropGroup(groupName);
            return propGroup.GetProperty(name);
        }
        public void SetExtPrperty(string name, object value, object? extent = null, bool fireChnagedEvent = true)
        {
            SetExtPrperty(string.Empty, name, value, extent, fireChnagedEvent);
        }
        public void SetExtPrperty(string groupName, string propName, object value, object? extent = null, bool fireChnagedEvent = true)
        {
            var propGroup = this.GetExtPropGroup(groupName);
            var prop = propGroup.GetProperty(propName);
            if (!fireChnagedEvent)
            {
                prop.Set(value, extent);
                OnPropertyChange(null,propGroup,prop);
            }
            else
            {
                bool chg_property = (value != prop.Value);
                if (chg_property)
                {
                    PropertyChangedBefore(propGroup, prop, prop.Value, value);
                    var oldValue = prop.Value;
                    prop.Set(value, extent);
                    PropertyChangedAfter(propGroup, prop, oldValue, prop.Value);
                }
                OnPropertyChange(null, propGroup, prop);
            }
        }


        public void PropertyChangedBefore(string propertyName, object oldValue, object newValue)
        {

            if (this.Document == null) return;
            var args = new PropertyChangedEventArgs
            {
                Object = this,
                PropertyName = propertyName,
                OldValue = oldValue,
                NewValue = newValue,
            };
            this.Document.OnPropertyChangedBefore(args);
        }
        public void PropertyChangedAfter(string propertyName, object oldValue, object newValue)
        {
            if (this.Document == null) return;
            var args = new PropertyChangedEventArgs
            {
                Object = this,
                PropertyName = propertyName,
                OldValue = oldValue,
                NewValue = newValue,
            };
            this.Document.OnPropertyChangedAfter(args);
        }
        public void PropertyChangedBefore(PropertyGroup propertyGroup, Property property, object oldValue, object newValue)
        {
            if (this.Document == null) return;
            var args = new PropertyChangedEventArgs
            {
                Object = this,
                ExtPropertyGroup = propertyGroup,
                ExtProperty = property,
                OldValue = oldValue,
                NewValue = newValue,
            };
            this.Document.OnPropertyChangedBefore(args);
        }
        public void PropertyChangedAfter(PropertyGroup propertyGroup, Property property, object oldValue, object newValue)
        {
            if (this.Document == null) return;
            var args = new PropertyChangedEventArgs
            {
                Object = this,
                ExtPropertyGroup = propertyGroup,
                ExtProperty = property,
                OldValue = oldValue,
                NewValue = newValue
            };
            this.Document.OnPropertyChangedBefore(args);
        }


        public virtual void Copy(LcadObject src)
        {
            this.Document = src.Document;
            this.PropsDef = src.PropsDef;
            if (src.ExtPropGroups != null)
            {
                this.ExtPropGroups = new PropertyGroupCollection();
                foreach (var pgrp in src.ExtPropGroups)
                {
                    this.ExtPropGroups.Add(pgrp.Clone());
                }
            }

        }


    }
}